﻿#include "Win32FileDialog.h"

#include <QDir>
#include <tchar.h>
#include <regex>
#include <iterator>

// 此回调函数为全局函数或静态函数;
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
    switch (uMsg)
    {
    case BFFM_INITIALIZED:
        {
            ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpData);
        }
        break;
    default:
        break;
    }
    return 0;
}

namespace Common {

	Win32FileDialog::Win32FileDialog()
	{
	}

	Win32FileDialog::~Win32FileDialog()
	{
	}

	bool Win32FileDialog::openFileDialog(LPTSTR path)
	{
		HRESULT result;
		// 返回与指定文件路径关联的 ITEMIDLIST 结构
		LPCITEMIDLIST pidlFile = ILCreateFromPath(path);
		// 初始化 COM 库
		CoInitialize(NULL);
		// 打开一个 Windows 资源管理器窗口，其中选定了特定文件夹中的指定项目
		result = SHOpenFolderAndSelectItems(pidlFile, 0, 0, 0);
		DWORD dw;
		if (result == S_OK)
		{
			// 关闭当前线程上的 COM 库
			CoUninitialize();
			return TRUE;
		}
		else
		{
			// 改文件不存在，获取上一级目录
			dw = _tcslen(path) - 1;
			for (; dw != -1; dw--)
			{
				if (path[dw] == '\\')
				{
					break;
				}
			}
			if (dw == -1)
			{
				return FALSE;
			}
		}

		IFileOpenDialog *pfd;
		result = CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, __uuidof(pfd), reinterpret_cast<void**>(&pfd));
		IShellWindows *psw;
		result = SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_IShellWindows, (void**)&psw));

		//设置文件夹路径
		TCHAR dir[MAX_PATH];
		_tcscpy(dir, path);
		dir[dw] = '\0';
		IShellItem *df = NULL;
		if (SUCCEEDED(SHCreateItemFromParsingName((PCWSTR)dir, NULL, IID_PPV_ARGS(&df))))
		{
			if (!dir)
				pfd->SetDefaultFolder(df);
			else
				pfd->SetFolder(df);
		}

		if (SUCCEEDED(result))
		{
			//pfd->SetTitle(title);
			if (SUCCEEDED(pfd->Show(NULL)))
			{
				IShellItem *psi;
				if (SUCCEEDED(pfd->GetResult(&psi)))
				{
					LPTSTR pszFilePath = NULL;
					if (SUCCEEDED(psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszFilePath)))
					{
						memcpy(path, pszFilePath, (_tcslen(pszFilePath) + 1) * sizeof(TCHAR));
						CoTaskMemFree(pszFilePath);
					}
					psi->Release();
				}
			}
			pfd->Release();
		}
		CoUninitialize();
		return SUCCEEDED(result);
	}

	bool Win32FileDialog::openFileDialog(QString path)
	{
		path = QDir::toNativeSeparators(path);
		return Win32FileDialog::openFileDialog((LPTSTR)path.toStdWString().c_str());
	}

	bool Win32FileDialog::openFileDialogOld(LPTSTR title, LPTSTR filter, LPTSTR initDir, LPTSTR path)
	{
		OPENFILENAME ofn = { 0 };
		TCHAR strFilename[MAX_PATH] = { 0 };    //用于接收文件名
		ofn.lStructSize = sizeof(OPENFILENAME); //结构体大小
		ofn.hwndOwner = GetForegroundWindow();   //拥有着窗口句柄
		ofn.lpstrTitle = title; //对话框标题
		ofn.lpstrFilter = convertFilterFormat(filter);;   //设置过滤
		ofn.nFilterIndex = 1;   //过滤器索引
		ofn.lpstrFile = strFilename;    //接收返回的文件名，注意第一个字符需要为NULL
		ofn.nMaxFile = sizeof(strFilename); //缓冲区长度
		ofn.lpstrInitialDir = initDir;  //初始目录为默认
		ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;   //文件、目录必须存在，隐藏只读选项
		if (!GetOpenFileName(&ofn))
		{
			delete[] ofn.lpstrFilter;
			return false;
		}
		memcpy(path, strFilename, (_tcslen(strFilename) + 1) * sizeof(TCHAR));
		delete[] ofn.lpstrFilter;
		return true;
	}

	bool Win32FileDialog::openFileDialogOld(QString title, QString filter, QString initDir, QString& path)
	{
		TCHAR filePathTemp[MAX_PATH] = { 0 };
		initDir = QDir::toNativeSeparators(initDir);
		bool bRet = Win32FileDialog::openFileDialogOld((LPTSTR)title.toStdWString().c_str(), (LPTSTR)filter.toStdWString().c_str(), (LPTSTR)initDir.toStdWString().c_str(), filePathTemp);
		path = path.fromStdWString(filePathTemp);
		path = QDir::fromNativeSeparators(path);
		return bRet;
	}

	bool Win32FileDialog::saveFileDialog(LPTSTR title, LPTSTR fileName, LPTSTR path, LPTSTR suffix, LPTSTR initDir)
	{
		// 初始化COM库
		CoInitialize(NULL);
		IFileSaveDialog *pfd;
		// 创建对话框实例
		HRESULT result = CoCreateInstance(__uuidof(FileSaveDialog), NULL, CLSCTX_ALL, __uuidof(pfd), reinterpret_cast<void**>(&pfd));
		if (SUCCEEDED(result))
		{
			pfd->SetTitle(title);
			pfd->SetFileName(fileName);
			pfd->SetDefaultExtension(L"*.*");

			// 添加文件过滤条件
			DWORD dwFlags;
			result = pfd->GetOptions(&dwFlags);
			result = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM | FOS_ALLOWMULTISELECT);

			// 分割字符串
			std::vector <std::wstring> suffixVec;
			std::wregex re(L"\\|");
			suffixVec = std::vector<std::wstring>{
				std::wcregex_token_iterator(suffix, suffix + wcslen(suffix), re, -1),
					std::wcregex_token_iterator()
			};

			std::wstring strSuffix;
			std::wstring strFileName = std::wstring(fileName);

			int nPos = std::wstring(fileName).rfind(L".");
			if (-1 != nPos)
			{
				strSuffix = strFileName.substr(nPos, strFileName.length() - nPos);
			}

			int fileTypeSize = (suffixVec.size() % 2) != 0 ? 1 : suffixVec.size() / 2;
			// 动态分配需要的过滤条件个数
			COMDLG_FILTERSPEC* fileType = new COMDLG_FILTERSPEC[fileTypeSize];
			// 当前后缀类型索引
			int nFileTypeIndex = 1;

			if (fileTypeSize != 1)
			{
				for (int i = 0; i < fileTypeSize; i++)
				{
					fileType[i].pszName = suffixVec[2 * i].c_str();
					fileType[i].pszSpec = suffixVec[2 * i + 1].c_str();

					if (!strSuffix.empty() && -1 != suffixVec[2 * i + 1].find(strSuffix))
					{
						nFileTypeIndex = i + 1;
					}
				}
			}
			else
			{
				fileType[0].pszName = L"All files";
				fileType[0].pszSpec = L"*.*";
			}

			result = pfd->SetFileTypes(fileTypeSize, fileType);
			result = pfd->SetFileTypeIndex(nFileTypeIndex);
			delete[] fileType;

			IShellItem *df = NULL;
			if (SUCCEEDED(SHCreateItemFromParsingName((PCWSTR)initDir, NULL, IID_PPV_ARGS(&df))))
			{
				if (!initDir)
					pfd->SetDefaultFolder(df);
				else
					pfd->SetFolder(df);
			}
			HWND hwndOwner = GetForegroundWindow();
			if (SUCCEEDED(pfd->Show(hwndOwner)))
			{
				IShellItem *psi;
				if (SUCCEEDED(pfd->GetResult(&psi)))
				{
					LPTSTR pszFilePath = NULL;
					if (SUCCEEDED(psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszFilePath)))
					{
						memcpy(path, pszFilePath, (_tcslen(pszFilePath) + 1) * sizeof(TCHAR));
						CoTaskMemFree(pszFilePath);
					}
					psi->Release();
				}
			}
			pfd->Release();
		}
		CoUninitialize();
		return SUCCEEDED(result);
	}

	bool Win32FileDialog::saveFileDialog(QString title, QString fileName, QString & path, QString filter, QString initDir)
	{
		//当前文件后缀
		QString curSuffix = QObject::tr("*.") + fileName.split(".").last();
		TCHAR filePathTemp[MAX_PATH] = { 0 };
		memcpy(filePathTemp, path.toStdWString().c_str(), path.size() * sizeof(TCHAR));
		initDir = QDir::toNativeSeparators(initDir);

		Win32FileDialog::saveFileDialog((LPTSTR)title.toStdWString().c_str(), (LPTSTR)fileName.toStdWString().c_str(), filePathTemp, (LPTSTR)filter.toStdWString().c_str(), (LPTSTR)initDir.toStdWString().c_str());
		path = path.fromStdWString(filePathTemp);
		path = QDir::fromNativeSeparators(path);
		if (!path.split("/").last().contains(QString("."), Qt::CaseInsensitive))
		{
			path += QString(".") + fileName.split(".").last();
		}
		return true;
	}

	bool Win32FileDialog::saveFileDialogOld(LPTSTR title, LPTSTR filter, LPTSTR initDir, LPTSTR path)
	{
		//打开文件
		OPENFILENAME ofn = { 0 };
		ofn.lStructSize = sizeof(OPENFILENAME); //结构体大小
		ofn.hwndOwner = GetForegroundWindow();   //拥有着窗口句柄
		ofn.lpstrTitle = title; //对话框标题
		ofn.lpstrFilter = convertFilterFormat(filter);   //设置过滤
		ofn.nFilterIndex = 1;   //过滤器索引
		ofn.lpstrFile = path;    //接收返回的文件名，注意第一个字符需要为NULL
		ofn.nMaxFile = MAX_PATH; //缓冲区长度
		ofn.lpstrInitialDir = initDir;  //初始目录为默认
		ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; //目录必须存在，覆盖文件前发出警告
		bool bRet = GetSaveFileName(&ofn);
		delete[] ofn.lpstrFilter;
		return bRet;
	}

	bool Win32FileDialog::saveFileDialogOld(QString title, QString filter, QString initDir, QString& path)
	{
		TCHAR filePathTemp[MAX_PATH] = { 0 };
		memcpy(filePathTemp, path.toStdWString().c_str(), path.size() * sizeof(TCHAR));
		initDir = QDir::toNativeSeparators(initDir);
		bool bRet = Win32FileDialog::saveFileDialogOld((LPTSTR)title.toStdWString().c_str(), (LPTSTR)filter.toStdWString().c_str(), (LPTSTR)initDir.toStdWString().c_str(), filePathTemp);
		path = path.fromStdWString(filePathTemp);
		path = QDir::fromNativeSeparators(path);
		return bRet;
	}

	bool Win32FileDialog::openFolderDialog(LPTSTR title, LPTSTR path, LPTSTR initDir)
	{
		CoInitialize(NULL);
		IFileOpenDialog *pfd;
		HRESULT result = CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, __uuidof(pfd), reinterpret_cast<void**>(&pfd));
		if (SUCCEEDED(result))
		{
			pfd->SetTitle(title);

			DWORD dwOptions;
			if (SUCCEEDED(pfd->GetOptions(&dwOptions)))
			{
				pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
			}


			IShellItem *df = NULL;
			if (SUCCEEDED(SHCreateItemFromParsingName((PCWSTR)initDir, NULL, IID_PPV_ARGS(&df))))
			{
				if (!initDir)
					pfd->SetDefaultFolder(df);
				else
					pfd->SetFolder(df);
			}

			HWND hwndOwner = GetForegroundWindow();
			if (SUCCEEDED(pfd->Show(hwndOwner)))
			{
				IShellItem *psi;
				if (SUCCEEDED(pfd->GetResult(&psi)))
				{
					LPTSTR pszFilePath = NULL;
					if (SUCCEEDED(psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszFilePath)))
					{
						memcpy(path, pszFilePath, (_tcslen(pszFilePath) + 1) * sizeof(TCHAR));
						CoTaskMemFree(pszFilePath);
					}
					psi->Release();
				}
			}
			pfd->Release();
		}
		CoUninitialize();
		return SUCCEEDED(result);
	}

	bool Win32FileDialog::openFolderDialog(QString title, QString &path, QString initDir)
	{
		TCHAR filePathTemp[MAX_PATH] = { 0 };
		initDir = QDir::toNativeSeparators(initDir);
		bool ret = openFolderDialog((LPTSTR)title.toStdWString().c_str(), filePathTemp, (LPTSTR)initDir.toStdWString().c_str());
		path = path.fromStdWString(filePathTemp);
		path = QDir::fromNativeSeparators(path);
		return ret;
	}

	bool Win32FileDialog::openFolderDialogOld(LPTSTR title, LPTSTR initDir, LPTSTR path)
	{
		TCHAR szBuffer[MAX_PATH] = { 0 };
		BROWSEINFO bi;
		ZeroMemory(&bi, sizeof(BROWSEINFO));
		bi.hwndOwner = GetForegroundWindow();
		bi.pszDisplayName = szBuffer;
		bi.pidlRoot = NULL;
		bi.lpszTitle = title;
		bi.ulFlags = BIF_NEWDIALOGSTYLE;;
		bi.lpfn = BrowseCallbackProc;
		bi.lParam = (LPARAM)initDir;
		LPITEMIDLIST idl = SHBrowseForFolder(&bi);
		if (NULL == idl)
		{
			return false;
		}
		SHGetPathFromIDList(idl, szBuffer);
		memcpy(path, szBuffer, (_tcslen(szBuffer) + 1) * sizeof(TCHAR));
		return true;
	}

	bool Win32FileDialog::openFolderDialogOld(QString title, QString initDir, QString& path)
	{
		TCHAR filePathTemp[MAX_PATH] = { 0 };
		initDir = QDir::toNativeSeparators(initDir);
		bool bRet = Win32FileDialog::openFolderDialogOld((LPTSTR)title.toStdWString().c_str(), (LPTSTR)path.toStdWString().c_str(), filePathTemp);
		path = path.fromStdWString(filePathTemp);
		path = QDir::fromNativeSeparators(path);
		return bRet;
	}

	LPTSTR Win32FileDialog::convertFilterFormat(LPTSTR filter)
	{
		size_t len = _tcslen(filter);
		LPTSTR format = new TCHAR[len + 2];
		for (int i = 0; i < len; ++i)
		{
			if (filter[i] == '|')
			{
				format[i] = '\0';
			}
			else
			{
				format[i] = filter[i];
			}
		}
		format[len] = '\0';
		format[len + 1] = '\0';
		return format;
	}

}//namespace Common
